You have a singly-linked list and want to check if it contains a cycle.
A singly-linked list is built with nodes, where each node has:
For example:
class LinkedListNode(object):
def __init__(self, value):
self.value = value
self.next = None
A cycle occurs when a node’s next points back to a previous node in the list. The linked list is no longer linear with a beginning and end—instead, it cycles through a loop of nodes.
Write a function contains_cycle() that takes the first node in a singly-linked list and returns a boolean indicating whether the list contains a cycle.
We keep two pointers to nodes (we'll call these “runners”), both starting at the first node. Every time slow_runner moves one node ahead, fast_runner moves two nodes ahead.
If the linked list has a cycle, fast_runner will "lap" (catch up with) slow_runner, and they will momentarily equal each other.
If the list does not have a cycle, fast_runner will reach the end.
def contains_cycle(first_node):
# Start both runners at the beginning
slow_runner = first_node
fast_runner = first_node
# Until we hit the end of the list
while fast_runner is not None and fast_runner.next is not None:
slow_runner = slow_runner.next
fast_runner = fast_runner.next.next
# Case: fast_runner is about to "lap" slow_runner
if fast_runner is slow_runner:
return True
# Case: fast_runner hit the end of the list
return False
time and space.
The runtime analysis is a little tricky. The worst case is when we do have a cycle, so we don't return until fast_runner equals slow_runner. But how long will that take?
First, we notice that when both runners are circling around the cycle fast_runner can never skip over slow_runner. Why is this true?
Suppose fast_runner had just skipped over slow_runner. fast_runner would only be 1 node ahead of slow_runner, since their speeds differ by only 1. So we would have something like this:
[ ] -> [s] -> [f]
What would the step right before this "skipping step" look like? fast_runner would be 2 nodes back, and slow_runner would be 1 node back. But wait, that means they would be at the same node! So fast_runner didn't skip over slow_runner! (This is a proof by contradiction.)
Since fast_runner can't skip over slow_runner, at most slow_runner will run around the cycle once and fast_runner will run around twice. This gives us a runtime of .
For space, we store two variables no matter how long the linked list is, which gives us a space cost of .
Some people have trouble coming up with the "two runners" approach. That's expected—it's somewhat of a blind insight. Even great candidates might need a few hints to get all the way there. And that's fine.
Remember that the coding interview is a dialogue, and sometimes your interviewer expects she'll have to offer some hints along the way.
One of the most impressive things you can do as a candidate is listen to a hint, fully understand it, and take it to its next logical step. Interview Cake gives you lots of opportunities to practice this. Don't be shy about showing lots of hints on our exercises—that's what they're there for!
Do you have an answer?
Wanna review this one again later? Or do you feel like you got it all?
Mark as done Pin for review laterReset editor
Powered by qualified.io